#include <string>
#include <iostream>
#include <sstream>
#include <stdexcept>
#include <list>

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>

#include "xmlConfig.h"

using namespace xercesc;
using namespace std;

xmlConfig::xmlConfig()
{
	try
     {            
         XMLPlatformUtils::Initialize();// Initialize Xerces infrastructure
     }
     catch(xercesc::XMLException & excp) 
     {
         char *msg = XMLString::transcode(excp.getMessage());
		 cerr << "XML toolkit initialization error: " << msg << endl;
         XMLString::release(&msg);
     }
	 TAG_AccountSystem = XMLString::transcode("AccountSystem");
	 TAG_IceServer = XMLString::transcode("IceServer");
	 TAG_Service = XMLString::transcode("Service");
	 TAG_Database = XMLString::transcode("Database");

	 parser = new XercesDOMParser;
}

xmlConfig::~xmlConfig()
{
	delete parser;
	//XMLString::release(&m_server); //?
	//XMLString::release(&m_service); //?
	//XMLString::release(&m_database); //?

	try
	{
		XMLString::release(&TAG_AccountSystem);
		XMLString::release(&TAG_IceServer);
		XMLString::release(&TAG_Service);
		XMLString::release(&TAG_Database);
	}
	catch( ... )
	{
		cerr << "Unkown exception encountered in TagNames release" << endl;
	}

	try
     {
         XMLPlatformUtils::Terminate();
     }
     catch(XMLException& excp)
     {
         char *msg = XMLString::transcode(excp.getMessage());
		 cerr << "XML toolkit terminate error: " << msg << endl;
         XMLString::release(&msg);
     }
}

int xmlConfig::readXmlConfig(string &configFile)
		throw(std::runtime_error)
{
	//
	struct stat fileStatus;

	int iretStat = stat(configFile.c_str(), &fileStatus);
	if(iretStat == ENOENT)
		throw ( std::runtime_error("configFile not exist || empty string"));
	else if(iretStat == ENOTDIR)
		throw ( std::runtime_error("path not a directory"));
	else if( iretStat == ELOOP)
		throw ( std::runtime_error("symbolic links error"));
	else if(iretStat == EACCES)
		throw ( std::runtime_error("Permission error"));
	else if(iretStat == ENAMETOOLONG)
		throw ( std::runtime_error("file read error"));

	//
	parser->setValidationScheme( XercesDOMParser::Val_Never);
	parser->setDoNamespaces( false );
	parser->setDoSchema( false );
	parser->setLoadExternalDTD( false );

	try 
	{
		parser->parse(configFile.c_str());
		
		DOMDocument *xmlDoc = parser->getDocument();
		if(!xmlDoc) throw (std::runtime_error("get XML error"));

		elementRoot = xmlDoc->getDocumentElement();
		if(!elementRoot) throw (std::runtime_error("empty XML document"));
	}
	catch(xercesc::XMLException & e)
	{
		char *msg = xercesc::XMLString::transcode(e.getMessage());
		cerr << "Error parsing file: " << msg << endl;
		XMLString::release(&msg);
	}

	return 1;
}

//////////////////////////////////////////////////////////////////////////////////////
int xmlConfig::getElementText(const DOMElement *pNode, string &text)
{
	if (pNode == NULL)
	{
		cout << "pNode is NULL" << endl;
		return -1;
	}
	
	const XMLCh* pText = pNode->getTextContent();
	if (pText == NULL)
	{
		cout << "XML getTextContent failed!" << endl;
		return -1; 
	}

	text = XMLString::transcode(pText);

	//printf("element value is %s\n", value);
	//XMLString::release(pText);
	
	return 0;
}

DOMElement *xmlConfig::getElementNode(const DOMElement *pNode, string nodeName)
{
	XMLCh *pXmlNodeName = XMLString::transcode(nodeName.c_str());

	//DOMException er(DOMException::NOT_FOUND_ERR);
		
	DOMNodeList *pNodeList = pNode->getElementsByTagName(pXmlNodeName);
	const XMLSize_t totleNode = pNodeList->getLength();
	DOMNode *pNodeSearch = 0;

	if (totleNode == 0)
	{
		cout << "No element node" << endl;
		return NULL;
	}

	for (XMLSize_t idx = 0; idx < totleNode; idx++)
	{
		pNodeSearch = pNodeList->item(idx);
		if ((pNodeSearch != NULL) && (DOMNode::ELEMENT_NODE == pNodeSearch->getNodeType()))
		{
			return (DOMElement*)pNodeSearch;
		}	       
	}
	return NULL;
}

/////////////////////////////////////////////////////////////////////////////////////////
int xmlConfig::getDatabaseConfig(DatabaseConfig &databaseConfig)
{
	int ret = -1;
	//if(databaseConfig.dbName = getDBName() ||
	//	databaseConfig.dbServer = getDBServer() ||
	//	databaseConfig.dbUser = getDBUser() ||
	//	databaseConfig.dbPasswd = getDBPasswd())
	databaseConfig.dbName = getDBName();
		
	databaseConfig.dbServer = getDBServer();
	
	databaseConfig.dbUser = getDBUser();
	
	databaseConfig.dbPasswd = getDBPasswd();
	
	//else
	//	ret = 0;

	return ret;
}

int xmlConfig::getICEServerConfig(ICEServerConfig &iceServerConfig)
{
	iceServerConfig.protoPort = getICEProtoPort();
	iceServerConfig.recvPort = getICERecvPort();
	iceServerConfig.transPort = getICETransPort();
	iceServerConfig.timeout = getICETimeout();

	return 1;
}

int xmlConfig::getRecvServiceConfig(RecvServiceConfig &recvServiceConfig)
{
	recvServiceConfig.dynamicLib = getRecvDynamicLib(); 
	recvServiceConfig.directory = getRecvDirectory();
	recvServiceConfig.recvProtoPort = getRecvProtoPort();

	return 1;
}

int xmlConfig::getTransServiceConfig(TransServiceConfig &transServiceConfig)
{
	transServiceConfig.dynamicLib = getTransDynamicLib(); 
	transServiceConfig.directory = getTransDirectory();
	transServiceConfig.transProtoPort = getTransProtoPort();

	return 1;
}
///////////////////////////////////////////////////////////////////////////////////
string xmlConfig::getDBName()
{
	int ret = -1;
	string dbNameData;
	try
	{
		DOMElement *dataBase = getElementNode(elementRoot, "Database");
		DOMElement *dbNameDom = getElementNode(dataBase, "DbName");		

		ret = getElementText(dbNameDom, dbNameData);
	}
	catch(DOMException &er)
	{
		char *msg = XMLString::transcode(er.getMessage());
		cerr << "XML error: " << msg << endl;
		XMLString::release(&msg);
		exit(-1);
	}
	if (ret == 0)
	{
		//cout << "dbName is: " << dbNameData.c_str(); 
		return dbNameData;
	}
	else
		return NULL;
}

string xmlConfig::getDBServer()
{
	int ret = -1;
	string serverData;
	try
	{
		DOMElement *dataBase = getElementNode(elementRoot, "Database");
		DOMElement *serverDom = getElementNode(dataBase, "Server");

		ret = getElementText(serverDom, serverData);
	}
	catch(DOMException &er)
	{
		char *msg = XMLString::transcode(er.getMessage());
		cerr << "XML error: " << msg << endl;
		XMLString::release(&msg);
		exit(-1);
	}
	if (ret == 0)
	{
		//cout << "server is " << serverData.c_str();	
		return serverData;
	}
	else
		return NULL;
}

string xmlConfig::getDBUser()
{
	int ret = -1;
	string userData;
	try
	{
		DOMElement *dataBase = getElementNode(elementRoot, "Database");
		DOMElement *userDom = getElementNode(dataBase, "User");

		ret = getElementText(userDom, userData);

	}
	catch(DOMException &er)
	{
		char *msg = XMLString::transcode(er.getMessage());
		cerr << "XML error: " << msg << endl;
		XMLString::release(&msg);
		exit(-1);
	}
	if (ret == 0)
	{
		//cout << "user is " << userData.c_str();	
		return userData;
	}
	else
		return NULL;
}

string xmlConfig::getDBPasswd()
{
	int ret = -1;
	string pwData;
	try
	{
		DOMElement *dataBase = getElementNode(elementRoot, "Database");
		DOMElement *passWdDom = getElementNode(dataBase, "Passwd");

		ret = getElementText(passWdDom, pwData);
	}
	catch(DOMException &er)
	{
		char *msg = XMLString::transcode(er.getMessage());
		cerr << "XML error: " << msg << endl;
		XMLString::release(&msg);
		exit(-1);
	}
	if (ret == 0)
	{
		//cout << "passwd is " << pwData.c_str();	
		return pwData;
	}
	else
		return NULL;
}
///////////////////////////////////////////////////////////////////////////////////////////
string xmlConfig::getICEProtoPort()
{
	int ret = -1;
	string iceProtoPortData;
	try
	{
		DOMElement *iceServer = getElementNode(elementRoot, "IceServer");
		DOMElement *protoPortDom= getElementNode(iceServer, "ProtoPort");		

		ret = getElementText(protoPortDom, iceProtoPortData);
	}
	catch(DOMException &er)
	{
		char *msg = XMLString::transcode(er.getMessage());
		cerr << "XML error: " << msg << endl;
		XMLString::release(&msg);
		exit(-1);
	}
	if (ret == 0)
	{
		//cout << "dbName is: " << dbNameData.c_str(); 
		return iceProtoPortData;
	}
	else
		return NULL;
}

string xmlConfig::getICERecvPort()
{
	int ret = -1;
	string iceRecvPortData;
	try
	{
		DOMElement *iceServer = getElementNode(elementRoot, "IceServer");
		DOMElement *recvPortDom= getElementNode(iceServer, "RecvPort");		

		ret = getElementText(recvPortDom, iceRecvPortData);
	}
	catch(DOMException &er)
	{
		char *msg = XMLString::transcode(er.getMessage());
		cerr << "XML error: " << msg << endl;
		XMLString::release(&msg);
		exit(-1);
	}
	if (ret == 0)
	{
		//cout << "dbName is: " << dbNameData.c_str(); 
		return iceRecvPortData;
	}
	else
		return NULL;
}

string xmlConfig::getICETransPort()
{
	int ret = -1;
	string iceTransPortData;
	try
	{
		DOMElement *iceServer = getElementNode(elementRoot, "IceServer");
		DOMElement *transPortDom= getElementNode(iceServer, "TransPort");		

		ret = getElementText(transPortDom, iceTransPortData);
	}
	catch(DOMException &er)
	{
		char *msg = XMLString::transcode(er.getMessage());
		cerr << "XML error: " << msg << endl;
		XMLString::release(&msg);
		exit(-1);
	}
	if (ret == 0)
	{
		//cout << "dbName is: " << dbNameData.c_str(); 
		return iceTransPortData;
	}
	else
		return NULL;
}

string xmlConfig::getICETimeout()
{
	int ret = -1;
	string iceTimeoutData;
	try
	{
		DOMElement *iceServer = getElementNode(elementRoot, "IceServer");
		DOMElement *timeoutDom= getElementNode(iceServer, "Timeout");		

		ret = getElementText(timeoutDom, iceTimeoutData);
	}
	catch(DOMException &er)
	{
		char *msg = XMLString::transcode(er.getMessage());
		cerr << "XML error: " << msg << endl;
		XMLString::release(&msg);
		exit(-1);
	}
	if (ret == 0)
	{
		//cout << "dbName is: " << dbNameData.c_str(); 
		return iceTimeoutData;
	}
	else
		return NULL;
}
///////////////////////////////////////////////////////////////////////////////////////////
string xmlConfig::getRecvDynamicLib()
{
	int ret = -1;
	string recvDynamicLibData;
	try
	{
		DOMElement *recvService = getElementNode(elementRoot, "RecvService");
		DOMElement *recvDynamicLibDom= getElementNode(recvService, "DynamicLib");		

		ret = getElementText(recvDynamicLibDom, recvDynamicLibData);
	}
	catch(DOMException &er)
	{
		char *msg = XMLString::transcode(er.getMessage());
		cerr << "XML error: " << msg << endl;
		XMLString::release(&msg);
		exit(-1);
	}
	if (ret == 0)
	{
		//cout << "dbName is: " << dbNameData.c_str(); 
		return recvDynamicLibData;
	}
	else
		return NULL;
}
string xmlConfig::getRecvDirectory()
{
	int ret = -1;
	string recvDirectoryData;
	try
	{
		DOMElement *recvService = getElementNode(elementRoot, "RecvService");
		DOMElement *recvDirectoryDom = getElementNode(recvService, "Directory");		

		ret = getElementText(recvDirectoryDom, recvDirectoryData);
	}
	catch(DOMException &er)
	{
		char *msg = XMLString::transcode(er.getMessage());
		cerr << "XML error: " << msg << endl;
		XMLString::release(&msg);
		exit(-1);
	}
	if (ret == 0)
	{
		//cout << "dbName is: " << dbNameData.c_str(); 
		return recvDirectoryData;
	}
	else
		return NULL;
}
string xmlConfig::getRecvProtoPort()
{
	int ret = -1;
	string recvProtoPortData;
	try
	{
		DOMElement *recvService = getElementNode(elementRoot, "RecvService");
		DOMElement *recvProtoPortDom= getElementNode(recvService, "RecvProtoPort");		

		ret = getElementText(recvProtoPortDom, recvProtoPortData);
	}
	catch(DOMException &er)
	{
		char *msg = XMLString::transcode(er.getMessage());
		cerr << "XML error: " << msg << endl;
		XMLString::release(&msg);
		exit(-1);
	}
	if (ret == 0)
	{
		//cout << "dbName is: " << dbNameData.c_str(); 
		return recvProtoPortData;
	}
	else
		return NULL;
}
/////////////////////////////////////////////////////////////////////////////////////////////
string xmlConfig::getTransDynamicLib()
{
	int ret = -1;
	string transDynamicLibData;
	try
	{
		DOMElement *transService = getElementNode(elementRoot, "TransService");
		DOMElement *transDynamicLibDom= getElementNode(transService, "DynamicLib");		

		ret = getElementText(transDynamicLibDom, transDynamicLibData);
	}
	catch(DOMException &er)
	{
		char *msg = XMLString::transcode(er.getMessage());
		cerr << "XML error: " << msg << endl;
		XMLString::release(&msg);
		exit(-1);
	}
	if (ret == 0)
	{
		//cout << "dbName is: " << dbNameData.c_str(); 
		return transDynamicLibData;
	}
	else
		return NULL;
}
string xmlConfig::getTransDirectory()
{
	int ret = -1;
	string transDirectoryData;
	try
	{
		DOMElement *transService = getElementNode(elementRoot, "TransService");
		DOMElement *transDirectoryDom= getElementNode(transService, "Directory");		

		ret = getElementText(transDirectoryDom, transDirectoryData);
	}
	catch(DOMException &er)
	{
		char *msg = XMLString::transcode(er.getMessage());
		cerr << "XML error: " << msg << endl;
		XMLString::release(&msg);
		exit(-1);
	}
	if (ret == 0)
	{
		//cout << "dbName is: " << dbNameData.c_str(); 
		return transDirectoryData;
	}
	else
		return NULL;
}
string xmlConfig::getTransProtoPort()
{
	int ret = -1;
	string transProtoPortData;
	try
	{
		DOMElement *transService = getElementNode(elementRoot, "TransService");
		DOMElement *transProtoPortDom= getElementNode(transService, "TransProtoPort");		

		ret = getElementText(transProtoPortDom, transProtoPortData);
	}
	catch(DOMException &er)
	{
		char *msg = XMLString::transcode(er.getMessage());
		cerr << "XML error: " << msg << endl;
		XMLString::release(&msg);
		exit(-1);
	}
	if (ret == 0)
	{
		//cout << "dbName is: " << dbNameData.c_str(); 
		return transProtoPortData;
	}
	else
		return NULL;
}
////////////////////////////////////////////////////////////////////////////////////////////
int main()
{
	string configFile="Account.xml";
	
	DatabaseConfig databaseStruct;
	ICEServerConfig iceServerStruct;
	RecvServiceConfig recvServiceStruct;
	TransServiceConfig transServiceStruct;

	xmlConfig obj;

	obj.readXmlConfig(configFile);
	obj.getDatabaseConfig(databaseStruct);
	obj.getICEServerConfig(iceServerStruct);
	obj.getRecvServiceConfig(recvServiceStruct);
	obj.getTransServiceConfig(transServiceStruct);

	cout << "dbName: " << databaseStruct.dbName << endl;
	cout << "dbServer: " << databaseStruct.dbServer << endl;
	cout << "dbUser: " << databaseStruct.dbUser << endl;
	cout << "dbPasswd: " << databaseStruct.dbPasswd << endl;
	
	cout << "ICE proto port: " << iceServerStruct.protoPort << endl;
	cout << "ICE recv port: " << iceServerStruct.recvPort << endl;
	cout << "ICE trans port: " << iceServerStruct.transPort << endl;
	cout << "ICE timeout : " << iceServerStruct.timeout << endl;

	cout << "Recv dynamiclib: " << recvServiceStruct.dynamicLib << endl;
	cout << "Recv directory: " << recvServiceStruct.directory << endl;
	cout << "Recv proto port: " << recvServiceStruct.recvProtoPort << endl;

	cout << "Trans dynamiclib: " << transServiceStruct.dynamicLib << endl;
	cout << "Trans directory: " << transServiceStruct.directory << endl;
	cout << "Trans proto Port: " << transServiceStruct.transProtoPort << endl;

	return 0;
}
